using System;
using System.Collections.Generic;
using System.Text;
using OpenALDotNet;
using OpenALDotNet.Streams;
using System.IO;
using Barrage.Global;
using System.Globalization;
using IWshRuntimeLibrary; 

namespace Barrage.Audios
{
    /// <summary>
    /// Structure with information about the track being played
    /// </summary>
    public struct TrackInfo
    {
        /// <summary>
        /// Artist Tag in file comments section
        /// </summary>
        public const string COMMENT_ARTIST = "ARTIST";
        /// <summary>
        /// Album Tag in file comments section
        /// </summary>
        public const string COMMENT_ALBUM = "ALBUM";
        /// <summary>
        /// Genre Tag in file comments section
        /// </summary>
        public const string COMMENT_GENRE = "GENRE";
        /// <summary>
        /// Title Tag in file comments section
        /// </summary>
        public const string COMMENT_TITLE = "TITLE";
        /// <summary>
        /// Date Tag in file comments section
        /// </summary>
        public const string COMMENT_DATE = "DATE";
        /// <summary>
        /// Track artist
        /// </summary>
        public string Artist;
        /// <summary>
        /// Track Album
        /// </summary>
        public string Album;
        /// <summary>
        /// Track Genre
        /// </summary>
        public string Genre;
        /// <summary>
        /// Track Title
        /// </summary>
        public string Title;
        /// <summary>
        /// Track Date
        /// </summary>
        public DateTime Date;
    }
    /// <summary>
    /// Jukebox loads a playlist of audio files from a specified folder and plays them at random. It's also capable of getting track information such as artist, genre, etc.
    /// </summary>
    public class Jukebox:Audio
    {
        const int BUFFER_SIZE = 1024 * 64;
        const int BUFFER_COUNT = 4;
        AudioSource source;
        AudioStreamPlayer player = null;
        VorbisAudioStream currentStream = null;
        TrackInfo currentTrackInfo = new TrackInfo();
        /// <summary>
        /// Returns information about the track currently played
        /// </summary>
        public TrackInfo CurrentTrackInfo { get { return currentTrackInfo; } }
        

        bool playing = false;
        List<string> playList=new List<string>();

        /// <summary>
        /// Returns the filename of the currently played song
        /// </summary>
        protected string CurrentSong { get { return playList[currentSong]; } }

        int currentSong = -1;
        Random random;

        string[] comments = null;

        /// <summary>
        /// Default constructor. Loads music from the standard music path
        /// </summary>
        public Jukebox()
            : this(Store.PathConfig[Store.MUSIC_PATH])
        {
        }

        /// <summary>
        /// Adds more songs to the playlist from the specified folder. Checks inside subfolders and navigates through shortcuts
        /// </summary>
        /// <param name="path">Folder containing songs or shortcuts to another folders / songs</param>
        /// <remarks>This function does not check for recursive shortcut inclusions, so it will hang given some cycle in them</remarks>
        public void AddMusicDir(string path)
        {
            
            DirectoryInfo di = new DirectoryInfo(path);
            if (!di.Exists)
            {
                // Check if it's a file
                FileInfo fi = new FileInfo(path);
                if (fi.Exists)
                    playList.Add(fi.FullName);
                return;
            }
            FileInfo[] files = di.GetFiles("*.ogg", SearchOption.AllDirectories);
            foreach (FileInfo fi in files)
            {
                playList.Add(fi.FullName);
            }
            WshShell shell = new WshShell();
            files = di.GetFiles("*.lnk");
            foreach (FileInfo fi in files)
            {
                IWshShortcut link = (IWshShortcut)shell.CreateShortcut(fi.FullName);
                // Check if the file is a directory
                FileInfo file = new FileInfo(link.TargetPath);
                if ((file.Attributes & FileAttributes.Directory)!=0)
                    AddMusicDir(file.FullName);
                else
                    if (string.Compare(file.Extension, ".ogg", true)==0)
                        playList.Add(file.FullName);
            }
        }

        /// <summary>
        /// Constructor. Takes an audio folder path
        /// </summary>
        /// <param name="path">Folder containing music</param>
        public Jukebox(string path)
        {
            source = new AudioSource();
            //source.EnqueueBuffer(file.Buffer);

            // Get OGG files
            AddMusicDir(path);
            
            // Also get files via shortcuts
            
            
            

            // Non spatialized sound
            source.IsRelative = false;
            // Do not loop
            source.IsLooping = false;

            random = new Random();
        }

        // The Jukebox can't be looped
        /// <summary>
        /// \internal Returns false as the jukebox can't be looped
        /// </summary>
        public override bool Loop { get { return false; } set { } }

        // The jukebox speed can't be modified
        /// <summary>
        /// \internal Always returns 1 as the jukebox' speed can't be modified
        /// </summary>
        public override float Speed{ get { return 1; } set { } }

        /// <summary>
        /// \internal Gets/Sets the music volume
        /// </summary>
        public override float Volume
        {
            get
            {
                return source.Gain;
            }
            set
            {
                source.Gain = value;
            }
        }

        /// <summary>
        /// \internal Returns true if the jukebox is playing
        /// </summary>
        public override bool Playing
        {
            get { return playing; }
        }

        /// <summary>
        /// \internal Starts the sound play
        /// </summary>
        public override void Start()
        {
            if (playList.Count > 0)
            {
                SetNextSong();
                playing = true;
            }
        }

        void ParseComments()
        {
            foreach (string s in comments)
            {
                string[] parts = s.Split(new char[]{'='}, 2,StringSplitOptions.None);
                if (parts.Length != 2) continue;
                switch (parts[0])
                {
                    case TrackInfo.COMMENT_ARTIST:
                        currentTrackInfo.Artist = parts[1];
                        break;
                    case TrackInfo.COMMENT_ALBUM:
                        currentTrackInfo.Album = parts[1];
                        break;
                    case TrackInfo.COMMENT_TITLE:
                        currentTrackInfo.Title = parts[1];
                        break;
                    case TrackInfo.COMMENT_GENRE:
                        currentTrackInfo.Genre = parts[1];
                        break;
                    case TrackInfo.COMMENT_DATE:
                        DateTime t;
                        if (DateTime.TryParse(parts[1], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out t)) currentTrackInfo.Date = t;
                        break;
                }
            }
        }

        /// <summary>
        /// Finishes current song and starts the next song.
        /// </summary>
        public void SetNextSong()
        {
            currentSong = GetNextSong();
            if (player == null)
            {
                currentStream  = new VorbisAudioStream(CurrentSong);
                comments = currentStream.Comments;
                ParseComments();
                player = new AudioStreamPlayer(currentStream, source, BUFFER_SIZE, BUFFER_COUNT);
                player.Play();
            }
            else
            {
                player.Stop();
                currentStream.Dispose();
                currentStream = new VorbisAudioStream(CurrentSong);
                player.Stream = currentStream;
                ParseComments();
                player.Reset();
            }
        }

        /// <summary>
        /// Gets a random song from the playlist, ensuring the next song isn't the one that's being played right now (except if the playlist has only one song)
        /// </summary>
        /// <returns>Song index in the playlist of the next song</returns>
        protected int GetNextSong()
        {
            // No song selected, set a directly random value
            if (currentSong == -1)
                return random.Next(playList.Count);
            
            // Ensure the same song is not repeated just after finishing
            // except if it's the only song...
            if (playList.Count == 1)
                return 0;

            int i = random.Next(playList.Count - 1);
            if (i >= currentSong) i++;
            return i;
        }

        /// <summary>
        /// \internal Stops the currently played song
        /// </summary>
        public override void Stop()
        {
            playing = false;
            if (player!=null)
                player.Stop();
            currentSong = -1;
        }

        /// <summary>
        /// \internal Updates sound
        /// </summary>
        /// <param name="item">Item used to place this audio</param>
        public override void Update(Item item)
        {
            if (player!=null)
            {
                if (source.SourceState == AudioSourceStateEnum.Stopped)
                    SetNextSong();
                player.Update();
            }
        }

        /// <summary>
        /// \internal Clear resources
        /// </summary>
        public override void Dispose()
        {
            source = null;
            currentStream = null;
            this.random = null;
            this.playList.Clear();
            player.Dispose();
            player = null;
            base.Dispose();
        }
    }
}
